#ifndef _FPDF_OBJECTS_
#define _FPDF_OBJECTS_

#ifndef _FX_BASIC_H_
#include "../fxcrt/fx_basic.h"
#endif

class CPDF_Document;
class CPDF_IndirectObjects;

class CPDF_Null;
class CPDF_Boolean;
class CPDF_Number;
class CPDF_String;
class CPDF_Stream;
class CPDF_StreamAcc;
class CPDF_StreamFilter;
class CPDF_Array;
class CPDF_Dictionary;
class CPDF_Reference;
class IPDF_DocParser;

class CPDF_ObjArchiveLoader;
class CPDF_GeneralStateData;

/** Object types in PDF. Return by CPDF_Object::GetType() function
*/
#define PDFOBJ_INVALID		0
#define	PDFOBJ_BOOLEAN		1
#define PDFOBJ_NUMBER		2
#define PDFOBJ_STRING		3
#define PDFOBJ_NAME			4
#define PDFOBJ_ARRAY		5
#define PDFOBJ_DICTIONARY	6
#define PDFOBJ_STREAM		7
#define PDFOBJ_NULL			8
#define PDFOBJ_REFERENCE	9

/** Base class for all PDF syntax objects */
class CPDF_Object : public CFX_Object
{
public:
	CPDF_Object(CFX_ByteString id) { m_ExternalID = id; }
	~CPDF_Object() {}

	/** Get type of the object. Returns one of the PDFOBJ_xxxx constants */
	int						GetType() const;

	/** Get indirect number of the object. 0 for direct object */
	FX_DWORD				GetObjNum() const;

	/** Compare with another object */
	FX_BOOL					IsIdentical(CPDF_Object*) const;

	/** Get a complete clone. The bDirect parameter specifies whether a totally direct copy
		is requested (without any reference inside, so the copy can be copied to other document) */
	CPDF_Object*			Clone(FX_BOOL bDirect = FALSE) const;

	/** Get a clone for direct object, or a reference for indirect object */
	CPDF_Object*			CloneRef(CPDF_Document* pDoc) const;

	/** Get direct object (the object data itself) of an object.
	    For direct object, return itself; for reference object, return the referred object
	*/
	CPDF_Object*			GetDirect() const;

	/** Release (free, destroy) the object. This function has no effect to indirect objects.
	*/
	void					Release();

	/** Get string value of the object. Applicable to string, name, and number objects
	    If object type not supported, empty string is returned.
	*/
	CFX_ByteString			GetString() const;

	/** Get Unicode text value of the object. Applicable to string and stream objects.
	    If object type not supported, empty string is returned.
		We assume the original text are encoding in PDF text encoding scheme.
		The returned text is encoded in UTF-16LE encoding.
		A character mapper can be used to convert the original text (if not already encoded in Unicode).
		If no character mapper used, PDFDocEncoding mapping is used.
	*/
	CFX_WideString			GetUnicodeText(const CFX_CharMap* pCharMap = NULL) const;

	/** Get float number value of the object. Applicable to number objects only
	    If object type is not number, 0.0f is returned.
		FIX: when FIX format is used, the returned value will be in FIX24.8 format.
	*/
	FX_FIXFLOAT				GetNumber() const;

#ifdef _USE_FIX_
	/** Get a FIX16.16 number if FIX format is used. When FIX format not used, this function is same as GetNumber() */
	FX_FIXFLOAT				GetNumber16() const;
	FX_FLOAT				GetFloat() const;
#else
	FX_FLOAT				GetFloat() const { return GetNumber(); }
#endif


	/** Get integer number value of the object. Applicable to number and boolean objects
	    If object type not supported, 0 is returned.
	*/
	int						GetInteger() const;

	/** Get dictionary value of the object. Applicable to dictionaries or stream only.
	    NULL is returned for other types of objects.
	*/
	CPDF_Dictionary*		GetDict() const;

	/** Get array value of the object. Applicable to arrays only.
	    NULL is returned for non-array objects.
  */
	CPDF_Array*				GetArray() const;

	/** Set string value into the object. Applicable to boolean, number, string and name objects.
	    For non-supported object types, this function does nothing
	*/
	void					SetString(const CFX_ByteString& str);

	/** Set text encoded in Unicode (UTF-16LE format). Applicable to string and stream objects.
	    "len" is number of characters, not bytes. -1 for null terminated string.
	*/
	void					SetUnicodeText(FX_LPCWSTR pUnicodes, int len=-1);

	/** Get type of direct object without loading it */
	int						GetDirectType() const;

	/** Set all reference objects' parent to an indirect object keeper */
	void					SetObjList(CPDF_IndirectObjects* pObjList);

	const void*				GetExternalID() const { return (FX_LPCSTR)m_ExternalID; }

	// added by XHL, 2010-04-08.
	FX_BOOL					IsModified() const;
protected:
	CPDF_Object() {}

	CFX_ByteString			m_ExternalID;
};

/** Derived objects */
class CPDF_Boolean : public CPDF_Object
{
public:
	static CPDF_Boolean*	Create(FX_BOOL value);

	FX_BOOL					Identical(CPDF_Boolean* pOther) const;

protected:
	CPDF_Boolean()  {}
	CPDF_Boolean(FX_BOOL value) {}
	~CPDF_Boolean() {}

	// added by XHL, 2010-04-08.
	friend class CPDF_Image;
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
	friend CPDF_Object* GetMemberObject(int index, const CPDF_GeneralStateData* pCurData);
};

class CPDF_Number : public CPDF_Object
{
public:
	static CPDF_Number*		Create(const CPDF_Number& src);
	static CPDF_Number*		Create(int value);
	static CPDF_Number*		Create(FX_FLOAT value);
	static CPDF_Number*		Create(FX_BSTR str);
	static CPDF_Number*		Create(FX_BOOL bInteger, void* data);

	FX_BOOL					Identical(CPDF_Number* pOther) const;

	CFX_ByteString			GetString() const;
	void					SetString(FX_BSTR str);

#ifdef _USE_FIX_
	FX_FIXFLOAT				GetNumber() const;
	void					SetNumber(FX_FIXFLOAT value);
	FX_FIXFLOAT16			GetNumber16() const;
	void					SetNumber16(FX_FIXFLOAT16 value);
	FX_FLOAT				GetFloat() const;
#else
	FX_FIXFLOAT				GetNumber() const;
	void					SetNumber(FX_FIXFLOAT value);
	FX_FIXFLOAT16			GetNumber16() const { return GetNumber(); }
	void					SetNumber16(FX_FIXFLOAT16 value) { SetNumber(value); }
	FX_FLOAT				GetFloat() const { return GetNumber(); }
#endif

	// added by XHL, 2010-04-08.
	FX_BOOL					IsInteger() const;
protected:
	CPDF_Number() {}
	CPDF_Number(int value) {}
	CPDF_Number(FX_FLOAT value) {}
	~CPDF_Number() {}

	// added by XHL, 2010-04-08.
	friend class CPDF_Document;
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
	friend CPDF_Object* GetMemberObject(int index, const CPDF_GeneralStateData* pCurData);
};

class CPDF_String : public CPDF_Object
{
public:
	static CPDF_String*		Create(const CFX_ByteString& str, FX_BOOL bHex = FALSE);
	static CPDF_String*		Create(const CFX_WideString& str);

	FX_BOOL					Identical(CPDF_String* pOther) const;

	// added by XHL, 2010-04-08.
	FX_BOOL					IsHex() const;
protected:
	CPDF_String() {}
	CPDF_String(const CFX_ByteString& str, FX_BOOL bHex = FALSE) {}
	~CPDF_String() {}

	// added by XHL, 2010-04-08.
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
};

class CPDF_Name : public CPDF_Object
{
public:
	static CPDF_Name*		Create(const CFX_ByteString& str);

	FX_BOOL					Identical(CPDF_Name* pOther) const;

protected:
	CPDF_Name() {}
	CPDF_Name(const CFX_ByteString& str) {}
	~CPDF_Name() {}

	// added by XHL, 2010-04-08.
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
	friend CPDF_Object* GetMemberObject(int index, const CPDF_GeneralStateData* pCurData);
};

class CPDF_Array : public CPDF_Object
{
public:
	static CPDF_Array*		Create();

	FX_DWORD				GetCount() const;

	/** Get reference to element */
	CPDF_Object*			GetElement(FX_DWORD index) const;	// Returns direct reference to the element
											// Don't call Release() for the returned object
	CPDF_Object*			GetElementValue(FX_DWORD index) const;	// Returns direct or referred indirect object
											// Don't call Release() for the returned object

	/** Get PDF common data types from an array */
	CFX_AffineMatrix		GetMatrix();
	CFX_FloatRect			GetRect();

	/** Get data from element */
	CFX_ByteString			GetString(FX_DWORD index) const;
	int						GetInteger(FX_DWORD index) const;
	FX_FIXFLOAT				GetNumber(FX_DWORD index) const;
	CPDF_Dictionary*		GetDict(FX_DWORD index) const;
	CPDF_Stream*			GetStream(FX_DWORD index) const;
	CPDF_Array*				GetArray(FX_DWORD index) const;

#ifndef _USE_FIX_
	FX_FLOAT				GetFloat(FX_DWORD index) const { return GetNumber(index); }
#else
	FX_FLOAT				GetFloat(FX_DWORD index) const;
#endif

	/** Modify elements */
	void					SetAt(FX_DWORD index, CPDF_Object* pObj);
	void					InsertAt(FX_DWORD index, CPDF_Object* pObj);
	void					RemoveAt(FX_DWORD index);

	/** Adding element. Please note all elements will be managed with the array object, so the object pointer
	    must NOT be freed by caller.
	*/
	void					Add(CPDF_Object* pObj);

	/** Adding different type of elements. Please use the following functions instead of the above generic
	    Add() function, whenever possible.
	*/
	void					AddNumber(FX_FIXFLOAT f);
	void					AddInteger(int i);
	void					AddString(const CFX_ByteString& str);
	void					AddName(const CFX_ByteString& str);
	void					AddReference(CPDF_IndirectObjects* pDoc, FX_DWORD objnum);
	void					AddReference(CPDF_IndirectObjects* pDoc, CPDF_Object* obj) { AddReference(pDoc, obj->GetObjNum()); }

	/** Compare value with another object */
#ifdef _USE_FIX_
	FX_FIXFLOAT16			GetNumber16(FX_DWORD index) const;
	void					AddNumber16(FX_FIXFLOAT16 value);
#else
	FX_FIXFLOAT16			GetNumber16(FX_DWORD index) const { return GetNumber(index); }
	void					AddNumber16(FX_FIXFLOAT16 value) { AddNumber(value); }
#endif

	FX_BOOL					Identical(CPDF_Array*) const;

protected:
	CPDF_Array() {}
	~CPDF_Array() {}

	// added by XHL, 2010-04-08.
	friend class CPDF_Document;
	friend class CPDF_Image;
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
	friend void _InsertWidthArray(HDC hDC, int start, int end, CPDF_Array* pWidthArray);
};

class CPDF_Dictionary : public CPDF_Object
{
public:
	static CPDF_Dictionary*	Create();

	/** Getting element pointers */
	CPDF_Object*			GetElement(FX_BSTR key) const;	// Get direct reference to the object (include reference)
											// Don't call Release() for the returned object
	CPDF_Object*			GetElementValue(FX_BSTR key) const;	// Returns direct or referred indirect object
											// Don't call Release() for the returned object

	/** Getting element data */
	CFX_ByteString			GetString(FX_BSTR key) const;
	CFX_ByteString			GetString(FX_BSTR key, FX_BSTR default_str) const;
	CFX_WideString			GetUnicodeText(FX_BSTR key, const CFX_CharMap* pCharMap = NULL) const;
	int						GetInteger(FX_BSTR key) const;
	int						GetInteger(FX_BSTR key, int default_int) const;
	FX_BOOL					GetBoolean(FX_BSTR key, FX_BOOL bDefault = FALSE) const;
	FX_FIXFLOAT				GetNumber(FX_BSTR key) const;
	CPDF_Dictionary*		GetDict(FX_BSTR key) const;
	CPDF_Stream*			GetStream(FX_BSTR key) const;
	CPDF_Array*				GetArray(FX_BSTR key) const;
	CFX_FloatRect			GetRect(FX_BSTR key) const;
	CFX_AffineMatrix		GetMatrix(FX_BSTR key) const;

#ifndef _USE_FIX_
	FX_FLOAT				GetFloat(FX_BSTR key) const { return GetNumber(key); }
#else
	FX_FLOAT				GetFloat(FX_BSTR key) const;
#endif

	FX_BOOL					KeyExist(FX_BSTR key) const;

	FX_POSITION				GetStartPos() const;
	CPDF_Object*			GetNextElement(FX_POSITION& pos, CFX_ByteString& key) const;// Returns direct reference to the element
											// Don't call Release() for the returned object

	/** Setting element data. Please note all elements will be managed with the dictionary object, so the object pointer
	    must NOT be freed by caller.
	*/
	void					SetAt(FX_BSTR key, CPDF_Object* pObj);

	/** Setting element of different types. Please use the following functions instead of the above generic
	    SetAt() function, whenever possible.
	*/
	void					SetAtName(FX_BSTR key, const CFX_ByteString& name);
	void					SetAtString(FX_BSTR key, const CFX_ByteString& string);
	void					SetAtInteger(FX_BSTR key, int i);
	void					SetAtNumber(FX_BSTR key, FX_FIXFLOAT f);
	void					SetAtReference(FX_BSTR key, CPDF_IndirectObjects* pDoc, FX_DWORD objnum);
	void					SetAtReference(FX_BSTR key, CPDF_IndirectObjects* pDoc, CPDF_Object* obj) {SetAtReference(key, pDoc, obj->GetObjNum());}
	void					SetAtRect(FX_BSTR key, const CFX_FloatRect& rect);
	void					SetAtMatrix(FX_BSTR key, const CFX_AffineMatrix& matrix);
	void					SetAtBoolean(FX_BSTR key, FX_BOOL bValue);

	void					RemoveAt(FX_BSTR key);
	void					ReplaceKey(FX_BSTR oldkey, FX_BSTR newkey);

	/** Compare value with another object */
	FX_BOOL					Identical(CPDF_Dictionary*) const;

	// added by XHL, 2010-04-08.
	void					AddValue(FX_BSTR key, CPDF_Object* pObj);
protected:
	CPDF_Dictionary() {}
	~CPDF_Dictionary();

	// added by XHL, 2010-04-08.
	friend class CFDF_Document;
	friend class CPDF_ContentGenerator;
	friend class CPDF_Creator;
	friend class CPDF_Document;
	friend class CPDF_Image;
	friend class CPDF_PageObjects;
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
};

class CPDF_Stream : public CPDF_Object
{
public:
	CPDF_Stream(FX_BYTE* pData, FX_DWORD size, CPDF_Dictionary* pDict);
	CPDF_Stream(CFX_ByteString& ExternalID);
	CPDF_Stream(class IFX_FileRead* pFile, class CPDF_CryptoHandler* pCrypto,
						FX_FILESIZE offset, FX_DWORD size, CPDF_Dictionary* pDict, FX_DWORD gennum) {}

	~CPDF_Stream();

	static CPDF_Stream*		Create(FX_BYTE* pData, FX_DWORD size, CPDF_Dictionary* pDict)
	{ return FX_NEW CPDF_Stream(pData, size, pDict); }

	CPDF_Dictionary*		GetDict() const;

	/** Set stream data. If pData is NULL, just allocate stream buffer.
		The data can be uncompressed or compressed. If it's uncompressed, then previous filter
		info will be dropped (if any). If it's compressed, the caller should also maintain
		the filter information in the dictionary.
		If bKeepBuf is TRUE, the buffer will be maintained and released by the stream object.
	*/
	void					SetData(FX_LPCBYTE pData, FX_DWORD size, FX_BOOL bCompressed, FX_BOOL bKeepBuf);

	/** Initialize a stream with data and dictionary.
	    If no dictionary specified, the old dictionary is retained
  */
	void					InitStream(FX_BYTE* pData, FX_DWORD size, CPDF_Dictionary* pDict);

	FX_BOOL					IsEncoded() const;

	FX_BOOL					Identical(CPDF_Stream*) const;	// Compare value with another object

	void					ResetFileStream(class IFX_FileRead* pFile, class CPDF_CryptoHandler* pCrypto,
								FX_FILESIZE offset, CPDF_Dictionary* pDict) {}
	
	// added by XHL, 2010-04-08.
	CPDF_StreamFilter*		GetStreamFilter(FX_BOOL bRaw) const;
	FX_DWORD				GetRawSize() const { return m_dwSize; }
	FX_BOOL					ReadRawData(FX_FILESIZE start_pos, FX_LPBYTE pBuf, FX_DWORD buf_size) const;
protected:
	FX_LPBYTE				m_pData;		// for memory based stream
	FX_DWORD				m_dwSize;		// for memory based stream
	CPDF_Dictionary*		m_pDict;		// for memory based stream

	// added by XHL, 2010-04-08.
	FX_DWORD				m_ObjNum;
	FX_DWORD				m_GenNum;
	CPDF_CryptoHandler*		m_pCryptoHandler;

	friend class CPDF_StreamAcc;
};

/** Accessor of CPDF_Stream: 
	This class depends on a stream, it maintains a buffer for the data of a stream.
	This buffer may be temporary, if the stream is encoded by one or more data encoders.
	This class doesn't decode any image encoders. */
class CPDF_StreamAcc : public CFX_Object
{
public:
	CPDF_StreamAcc();
	~CPDF_StreamAcc();

	void					LoadAllData(const CPDF_Stream* pStream, FX_BOOL bRawAccess = FALSE, 
									FX_DWORD estimated_size = 0, FX_BOOL bImageAcc = FALSE);

	const CPDF_Stream*		GetStream() const { return m_pStream; }
	CPDF_Dictionary*		GetDict() const { return m_pStream->GetDict(); }
	FX_LPCBYTE				GetData() { return m_pData; }
	FX_DWORD				GetSize() { return m_dwSize; }

	const CFX_ByteString&	GetImageDecoder() { return m_ImageDecoder; }
	const CPDF_Dictionary*	GetImageParam() { return m_pImageParam; }

protected:
	const CPDF_Stream*		m_pStream;
	FX_BOOL					m_bNewBuf;
	FX_LPBYTE				m_pData;
	FX_DWORD				m_dwSize;
	CFX_ByteString			m_ImageDecoder;
	CPDF_Dictionary*		m_pImageParam;

	friend class CPDF_StreamAcc;
};

/** @brief Internally estimated cached buffer size for stream filter class. */
#define FPDF_FILTER_BUFFER_SIZE		20480

/** @brief Data filter created for accessing PDF stream data. */
class CPDF_StreamFilter : public CFX_Object
{
public:
	/** The destructor. */
	~CPDF_StreamFilter();

	/**
	 * Read a data block. Return number of bytes actually read.
	 * If read size is less than the asked size, it indicates EOF.
	 *
	 * @param[out] buffer	It receives the read data.
	 * @param[in] size		The size in bytes to read.
	 * @return The number of bytes actually read.
	 */
	FX_DWORD			ReadBlock(FX_LPBYTE buffer, FX_DWORD size);

	/** Get current source position (in the raw data stream). */
	FX_DWORD			GetSrcPos() { return m_SrcOffset; }

	/** Get the stream object. */
	const CPDF_Stream*	GetStream() { return m_pStream; }

protected:
	/** The constructor. The object can't be constructed directly. */
	CPDF_StreamFilter() {}
	/**
	 * Read data left over.
	 *
	 * @param[out] buffer	It receives the read data.
	 * @param[in] buf_size	The size in bytes to read.
	 * @return The number of bytes actually read.
	 */
	FX_DWORD			ReadLeftOver(FX_LPBYTE buffer, FX_DWORD buf_size);

	/** The stream object. */
	const CPDF_Stream*	m_pStream;
	/** The data filter chain. */
	CFX_DataFilter*		m_pFilter;
	/** The cached decoded data. */
	CFX_BinaryBuf*		m_pBuffer;
	/** The offset of the cached decoded data in the whole decoded stream data. */
	FX_DWORD			m_BufOffset;
	/** The offset of the cached source encoded data in the whole source encoded stream data. */
	FX_DWORD			m_SrcOffset;
	/** The cached source encoded data. */
	FX_BYTE				m_SrcBuffer[FPDF_FILTER_BUFFER_SIZE];

	friend class CPDF_Stream;
};

class CPDF_Null : public CPDF_Object
{
public:
	static CPDF_Null*		Create();

protected:
	CPDF_Null()  {}
	~CPDF_Null()  {}

	// added by XHL, 2010-04-08.
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
};

class CPDF_Reference : public CPDF_Object
{
public:
	static CPDF_Reference*	Create(CPDF_IndirectObjects* pDoc, int objnum) { return NULL; }

	CPDF_IndirectObjects*	GetObjList() { return NULL; }
	FX_DWORD				GetRefObjNum() const;
	void					SetRef(CPDF_IndirectObjects* pDoc, FX_DWORD objnum);

	FX_BOOL					Identical(CPDF_Reference* pOther) const;

protected:
	CPDF_Reference() {}
	CPDF_Reference(CPDF_IndirectObjects* pDoc, int objnum) {}
	~CPDF_Reference() {}

	// added by XHL, 2010-04-08.
	friend class CPDF_Document;
	friend CPDF_ObjArchiveLoader& operator >> (CPDF_ObjArchiveLoader& ar, CPDF_Object*& pObj);
	friend void InsertDeletePDFPage(CPDF_Document* pDoc, CPDF_Dictionary* pPages, int nPagesToGo, CPDF_Dictionary* pPage, FX_BOOL bInsert);
};

/** Class for a PDF indirect object collection. This is a base class for both CPDF_Document and CFDF_Document */
class CPDF_IndirectObjects : public CFX_LockObject
{
public:
	CPDF_IndirectObjects(IPDF_DocParser* pParser);
	~CPDF_IndirectObjects();

	/** Load an indirect object by an object number */
	CPDF_Object*			GetIndirectObject(FX_DWORD objnum);

	/** Get type of an indirect object, without loading the object content.
	*/
	int						GetIndirectType(FX_DWORD objnum);

	/** Add an object to indirect object list. The new object number is returned.
	*/
	FX_DWORD				AddIndirectObject(CPDF_Object* pObj);

	/** Get last assigned indirect object number */
	FX_DWORD				GetLastObjNum() const;

	void					ReloadFileStreams(){}

	// added by XHL, 2010-04-08.
	void					ReleaseIndirectObject(FX_DWORD objnum);
	void					DeleteIndirectObject(FX_DWORD objnum);
	CPDF_Object*			ImportExternalObject(CPDF_Object* pObj, CFX_MapPtrToPtr* pMapping);
	void					InsertIndirectObject(FX_DWORD objnum, CPDF_Object* pObj);
protected:
	/** A parser served as a source of indirect objects. NULL for FDF document. */
	IPDF_DocParser*			m_pParser;
  
	// added by XHL, 2010-04-08.
	CFX_MapPtrToPtr			m_IndirectObjs;
	FX_DWORD				m_LastObjNum;
};

#endif		// _FPDF_OBJECTS_
